package com.griddynamics.jagger.master;
import com.griddynamics.jagger.jaas.storage.model.TestExecutionEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* Handles communication to JaaS execution API.
*/
public class JaasExecApiClient {
private static final Logger LOGGER = LoggerFactory.getLogger(JaasExecApiClient.class);
private final URI executionUri;
private final RestTemplate restTemplate = new RestTemplate();
public JaasExecApiClient(String executionId, String jaasEndpoint) {
URI jaasEndpointUri;
try {
jaasEndpointUri = new URI(jaasEndpoint);
} catch (URISyntaxException e) {
throw new IllegalStateException("Incorrect JaaS endpoint", e);
}
this.executionUri = UriComponentsBuilder.newInstance()
.uri(jaasEndpointUri)
.path("/executions")
.path("/" + executionId)
.build()
.toUri();
}
public Optional<TestExecutionEntity> getExecution() {
try {
return doWithRetries(rt -> restTemplate.getForEntity(executionUri, TestExecutionEntity.class).getBody());
} catch (HttpClientErrorException e) {
LOGGER.error("A client error during a GET request to {}.", executionUri, e);
throw e;
}
}
public void startExecution() {
Optional<TestExecutionEntity> executionEntity = getExecution();
executionEntity.ifPresent(entity -> {
entity.setStatus(TestExecutionEntity.TestExecutionStatus.RUNNING);
LOGGER.info("Starting execution of {}", entity);
updateExecution(entity);
});
}
public void completeExecution(final String sessionId) {
Optional<TestExecutionEntity> executionEntity = getExecution();
executionEntity.ifPresent(entity -> {
entity.setStatus(TestExecutionEntity.TestExecutionStatus.COMPLETED);
entity.setSessionId(sessionId);
LOGGER.info("Updating execution of {}", entity);
updateExecution(entity);
});
}
public void failExecution(final String errorMessage) {
Optional<TestExecutionEntity> executionEntity = getExecution();
executionEntity.ifPresent(entity -> {
entity.setStatus(TestExecutionEntity.TestExecutionStatus.FAILED);
entity.setErrorMessage(errorMessage);
LOGGER.info("Failing execution of {}", entity);
updateExecution(entity);
});
}
private void updateExecution(TestExecutionEntity executionEntity) {
try {
doWithRetries(rt -> {
restTemplate.put(executionUri, executionEntity);
return null;
});
} catch (HttpClientErrorException e) {
LOGGER.error("A client error during a PUT request to {} with body {}", executionUri, executionEntity, e);
}
}
private <T> Optional<T> doWithRetries(Function<RestTemplate, T> function) {
int retriesTimeout = 10;
int retriesLeft = 5;
do {
try {
return Optional.ofNullable(function.apply(restTemplate));
} catch (HttpServerErrorException e) { // will retry in case that is a temporarily issue
LOGGER.warn("A server error during a request", e);
try {
TimeUnit.SECONDS.sleep(retriesTimeout);
retriesLeft--;
retriesTimeout *= 2;
} catch (InterruptedException e1) {
retriesLeft = 0;
}
}
}
while (retriesLeft > 0);
return Optional.empty();
}
}